#!/bin/bash

:<<memoName

awk 是行处理器，相比较屏幕处理的优点，在处理庞大文件时不会出现内存溢出或是处理缓慢的问题，通常用来格式化文本信息。
awk 处理过程: 依次对每一行进行处理，然后输出。

awk 一次对一条记录进行操作，直到到达输入的末尾。
记录由一个称为记录分隔符的字符分隔。
默认的记录分隔符是换行符，这意味着文本数据中的每一行都是一个记录。

每条记录中的字段由$后跟字段号（以1开头）引用。第一个字段用$1表示，第二个字段用$2表示，依此类推。
最后一个字段也可以用特殊变量$NF引用。整个记录可以用$0引用。

awk 支持不同类型的模式，包括正则表达式、关系表达式、范围和特殊表达式模式。
在下面的例子中，将创建一个名为“ teams.txt”的文件，文件内容如下：
	[root@localhost ~]# cat teams.txt
	Bucks Milwaukee    60 22 0.732 
	Raptors Toronto    58 24 0.707 
	76ers Philadelphia 51 31 0.622
	Celtics Boston     49 33 0.598
	Pacers Indiana     48 34 0.585

正则表达式模式
	awk的正则表达式模式用斜线（//）包含起来，例如：要显示包含“0.5”的每条记录，可以运行以下命令：
	[root@localhost ~]# awk '/0.5/ {print $0}' teams.txt 
	Celtics Boston     49 33 0.598
	Pacers Indiana     48 34 0.585

	要显示包含“0.5”的每条记录的第一个和第二个字段：
	[root@localhost ~]# awk '/0.5/ {print $1,$2}' teams.txt 
	Celtics Boston
	Pacers Indiana

	显示一个或者多个数字开头的记录，并打印第一列：
	[root@localhost ~]# awk '/^[0-9]/ {print $1}' teams.txt 
	76ers

关系表达式模式
	关系表达式模式通常用于匹配特定字段或变量的内容。
		常用的关系运算符有：
			~：包含
			!~：不包含
			>：大于
			<：小于
			>=：大于等于
			<=：小于等于
			=：等于
			!=：不等于
	使用~“包含”比较运算符。例如，显示第二个字段中包含“ia”字符的每条记录：
	[root@localhost ~]# awk '$2 ~ /ia/ {print $0}' teams.txt 
	76ers Philadelphia 51 31 0.622
	Pacers Indiana     48 34 0.585 

	若要匹配不包含给定字符的记录，可以使用!~不包含操作符。
	例如，显示第二个字段中不包含“ia”字符的每条记录：
	[root@localhost ~]# awk '$2 !~ /ia/ {print $0}' teams.txt 
	Bucks Milwaukee    60 22 0.732 
	Raptors Toronto    58 24 0.707 
	Celtics Boston     49 33 0.598 

	使用!=“不等于”操作符，
	[root@localhost ~]# awk '$3 != 60 {print $0}' teams.txt 
	Raptors Toronto    58 24 0.707 
	76ers Philadelphia 51 31 0.622
	Celtics Boston     49 33 0.598
	Pacers Indiana     48 34 0.585

	使用>=“大于等于”操作符，显示第四个字段大于等于30的记录：
	[root@localhost ~]# awk '$4 >= 30 {print $0}' teams.txt 
	76ers Philadelphia 51 31 0.622
	Celtics Boston     49 33 0.598
	Pacers Indiana     48 34 0.585

范围模式
	范围模式由逗号分隔的两组字符组成，从与第一个字符串匹配的记录开始，直到与第二个字符串的记录匹配为止的所有记录。
	例如，显示从“Raptors”到 “Celtics”在内的记录，：
		[root@localhost ~]# awk '/Raptors/,/Celtics/ {print $0}' teams.txt 
		Raptors Toronto    58 24 0.707 
		76ers Philadelphia 51 31 0.622
		Celtics Boston     49 33 0.598

	范围模式也可以使用关系表达式，例如，显示第四个字段等于31到第四个字段等于34 的记录：
		[root@localhost ~]# awk '$4 == 31 , $4 == 34 {print $0}' teams.txt 
		76ers Philadelphia 51 31 0.622
		Celtics Boston     49 33 0.598
		Pacers Indiana     48 34 0.585

特殊表达式模式
	awk包括以下特殊模式。
		BEGIN：在处理记录之前执行此操作。
		END：用于在处理记录之后执行此操作。

		BEGIN模式通常用于设置变量，END模式用于处理统计的数据。例如，下面将显示“Start Processing.”，然后显示第三个字段，最后显示”End Processing.”：
			[root@localhost ~]# awk 'BEGIN {print "Start Processing."};{print $3};END {print "End Processing."}' teams.txt 
			Start Processing.
			60
			58
			51
			49
			48
			End Processing.

内置变量
	awk有许多内置变量，允许控制程序的处理方式。下面是一些常见的内置变量:
		NF：记录中的字段数
		NR：当前记录的编号
		FILENAME：当前正在处理的输入文件名称
		FS：字段分隔符
		RS：记录分隔符
		OFS：输出字段分隔符
		ORS：输出记录分隔符
		下面是一个显示如何打印文件名和行数的示例：
		[root@localhost ~]# awk 'END {print "File" , FILENAME , "contains" , NR , "lines."}' teams.txt 
		File teams.txt contains 5 lines.

awk命令形式:
	awk [-F|-f|-v] ‘BEGIN{} //{command1; command2} END{}’ file
	 [-F|-f|-v]   大参数，-F指定分隔符，-f调用脚本，-v定义变量 var=value
	'  '          引用代码块
	BEGIN   初始化代码块，在对每一行进行处理之前，初始化代码，主要是引用全局变量，设置FS分隔符
	//           匹配代码块，可以是字符串或正则表达式
	{}           命令代码块，包含一条或多条命令
	；           多条命令使用分号分隔
	END      结尾代码块，在对每一行进行处理之后再执行的代码块，主要是进行最终计算或输出结尾摘要信息
 
特殊要点:
	$0              表示整个当前行
	$1              每行第一个字段
	NF              字段数量变量
	NR              每行的记录号，多文件记录递增
	FNR             与NR类似，不过多文件记录不递增，每个文件都从1开始
	\t              制表符
	\n              换行符
	FS              BEGIN时定义分隔符
	RS              输入的记录分隔符， 默认为换行符(即文本是按一行一行输入)
	~               匹配，与==相比不是精确比较
	!~              不匹配，不精确比较
	==              等于，必须全部相等，精确比较
	!=              不等于，精确比较
	&&　            逻辑与
	||              逻辑或
	+               匹配时表示1个或1个以上
	/[0-9][0-9]+/   两个或两个以上数字
	/[0-9][0-9]*/   一个或一个以上数字
	FILENAME        文件名
	OFS             输出字段分隔符， 默认也是空格，可以改为制表符等
	ORS             输出的记录分隔符，默认为换行符,即处理结果也是一行一行输出到屏幕
	-F'[:#/]'       定义三个分隔符
 
print & $0
print 是awk打印指定内容的主要命令
	awk '{print}'                         /etc/passwd   ==   awk '{print $0}'  /etc/passwd     // 命令结果相等
	awk '{print " "}'                     /etc/passwd                                          // 不输出passwd的内容，而是输出相同个数的空行，进一步解释了awk是一行一行处理文本
	awk '{print "a"}'                     /etc/passwd                                          // 输出相同个数的a行，一行只有一个a字母
	awk -F":" '{print $1}'                /etc/passwd
	awk -F: '{print $1; print $2}'        /etc/passwd                                          // 将每一行的前二个字段，分行输出，进一步理解一行一行处理文本
	awk -F: '{print $1,$3,$6}' OFS="\t"   /etc/passwd                                          // 输出字段1,3,6，以制表符作为分隔符
 
-f 指定脚本文件             //效果与awk -F":" '{print $1}'相同,只是分隔符使用FS在代码自身中指定
awk -f script.awk  file
	BEGIN{
	FS=":"
	}
	{print $1}               

awk 'BEGIN{X=0} /^$/{ X+=1 } END{print "I find",X,"blank lines."}' test 
  I find 4 blank lines.

ls -l | awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is",sum}'                       // 计算文件大小
  total size is 17487
 
-F 指定分隔符
$1 指指定分隔符后，第一个字段，$3第三个字段， \t是制表符
一个或多个连续的空格或制表符看做一个定界符，即多个空格看做一个空格
	awk -F":" '{print $1}'                             /etc/passwd
	awk -F ":" '{print $1}'                            /etc/passwd
	awk -F ':' '{print $1}'                            /etc/passwd
	awk -F':' '{print $1}'                             /etc/passwd
	awk -F":" '{print $1 $3}'                          /etc/passwd  // $1与$3相连输出，不分隔
	awk -F":" '{print $1$3}'                           /etc/passwd  // $1与$3相连输出，不分隔
	awk -F":" '{print $1,$3}'                          /etc/passwd  // 多了一个逗号，$1与$3使用空格分隔
	awk -F":" '{print $1 " " $3}'                      /etc/passwd  // $1与$3之间手动添加空格分隔
	awk -F":" '{print "Username:" $1 "\t\t Uid:" $3 }' /etc/passwd  // 自定义输出  
	awk -F: '{print NF}'                               /etc/passwd  // 显示每行有多少字段
	awk -F: '{print $NF}'                              /etc/passwd  // 将每行第NF个字段的值打印出来
	awk -F: 'NF==4 {print }'                           /etc/passwd  // 显示只有4个字段的行
	awk -F: 'NF>2{print $0}'                           /etc/passwd  // 显示每行字段数量大于2的行
	awk '{print NR,$0}'                                /etc/passwd  // 输出每行的行号
	awk -F: '{print NR,NF,$NF,"\t",$0}'                /etc/passwd  // 依次打印行号，字段数，最后字段值，制表符，每行内容
	awk -F: 'NR==5 {print}'                            /etc/passwd  // 显示第5行
	awk -F: 'NR==15 {print}'                           /etc/passwd  // 显示第15行
	awk -F : 'NR==15 {print}'                          /etc/passwd  // 显示第15行
	awk -F':' 'NR==15 {print}'                         /etc/passwd  // 显示第15行
	awk -F ':' 'NR==15 {print}'                        /etc/passwd  // 显示第15行
	awk -F":" 'NR==15 {print}'                         /etc/passwd  // 显示第15行
	awk -F ":" 'NR==15 {print}'                        /etc/passwd  // 显示第15行
	awk -F ":" 'NR==15 {print $1}'                     /etc/passwd  // 显示第15行 的 $1 字段
	awk -F ":" 'NR==15 {print $1,$5}'                  /etc/passwd  // 显示第15行 的 $1 $5 字段
	awk -F ":" 'NR==15 {print $1 $5}'                  /etc/passwd  // 显示第15行 的 $1 $5 字段
	awk -F: 'NR==5 || NR==6 {print}'                   /etc/passwd  // 显示第5行和第6行
	route -n | awk 'NR!=1 {print}'                                  // 不显示第一行
 
//匹配代码块
//纯字符匹配   !//纯字符不匹配   ~//字段值匹配    !~//字段值不匹配   ~/a1|a2/字段值匹配a1或a2   
	awk '/mysql/'                        /etc/passwd
	awk '/mysql/{print }'                /etc/passwd
	awk '/mysql/{print $0}'              /etc/passwd   // 三条指令结果一样
	awk '!/mysql/{print $0}'             /etc/passwd   // 输出不匹配mysql的行
	awk '/mysql|mail/{print}'            /etc/passwd
	awk '!/mysql|mail/{print}'           /etc/passwd
	awk -F: '/mail/,/mysql/{print}'      /etc/passwd   // 区间匹配
	awk '/[2][7][7]*/{print $0}'         /etc/passwd   // 匹配包含27为数字开头的行，如27，277，2777...
	awk -F: '$1~/mail/{print $1}'        /etc/passwd   // $1匹配指定内容才显示
	awk -F: '{if($1~/mail/) print $1}'   /etc/passwd   // 与上面相同
	awk -F: '$1!~/mail/{print $1}'       /etc/passwd   // 不匹配
	awk -F: '$1!~/mail|mysql/{print $1}' /etc/passwd        
 
IF语句
必须用在{}中，且比较内容用()扩起来
	awk -F: '{if($1~/mail/) print $1}'                    /etc/passwd                                       //简写
	awk -F: '{if($1~/mail/) {print $1}}'                  /etc/passwd                                   //全写
	awk -F: '{if($1~/mail/) {print $1} else {print $2}}'  /etc/passwd            //if...else...
 
 
条件表达式
	==   !=   >   >=  
	awk -F":" '$1=="mysql"{print $3}'        /etc/passwd  
	awk -F":" '{if($1=="mysql") print $3}'   /etc/passwd  //与上面相同 
	awk -F":" '$1!="mysql"{print $3}'        /etc/passwd  //不等于
	awk -F":" '$3>1000{print $3}'            /etc/passwd  //大于
	awk -F":" '$3>=100{print $3}'            /etc/passwd  //大于等于
	awk -F":" '$3<1{print $3}'               /etc/passwd  //小于
	awk -F":" '$3<=1{print $3}'              /etc/passwd  //小于等于
 
逻辑运算符
&&　|| 
	awk -F: '$1~/mail/ && $3>8 {print }'        /etc/passwd  //逻辑与，$1匹配mail，并且$3>8
	awk -F: '{if($1~/mail/ && $3>8) print }'    /etc/passwd
	awk -F: '$1~/mail/ || $3>1000 {print }'     /etc/passwd  //逻辑或
	awk -F: '{if($1~/mail/ || $3>1000) print }' /etc/passwd 
 
数值运算
	awk -F: '$3 > 100'                          /etc/passwd    
	awk -F: '$3 > 100 || $3 < 5'                /etc/passwd  
	awk -F: '$3+$4 > 200'                       /etc/passwd
	awk -F: '/mysql|mail/{print $3+10}'         /etc/passwd       //第三个字段加10打印 
	awk -F: '/mysql/{print $3-$4}'              /etc/passwd       //减法
	awk -F: '/mysql/{print $3*$4}'              /etc/passwd       //求乘积
	awk '/MemFree/{print $2/1024}'              /proc/meminfo     //除法
	awk '/MemFree/{print int($2/1024)}'         /proc/meminfo     //取整
 
输出分隔符OFS
	awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS="\t"   netstat.txt
	awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS="\t"  netstat.txt        
//输出字段6匹配WAIT的行，其中输出每行行号，字段4，5,6，并使用制表符分割字段
 
输出处理结果到文件
①在命令代码块中直接输出        route -n | awk 'NR!=1{print > "./fs"}'   
②使用重定向进行输出           route -n | awk 'NR!=1{print}'  > ./fs

格式化输出
	netstat -anp | awk '{printf "%-8s %-8s %-10s\n",$1,$2,$3}' 
		printf表示格式输出
		%格式化输出分隔符
		-8长度为8个字符
		s表示字符串类型
打印每行前三个字段，指定第一个字段输出字符串类型(长度为8)，第二个字段输出字符串类型(长度为8),
第三个字段输出字符串类型(长度为10)
	netstat -anp | awk '$6=="LISTEN" || NR==1 {printf "%-10s %-10s %-10s \n",$1,$2,$3}'
	netstat -anp | awk '$6=="LISTEN" || NR==1 {printf "%-3s %-10s %-10s %-10s \n",NR,$1,$2,$3}'
 
IF语句
	awk -F: '{if($3>100) print "large"; else print "small"}' /etc/passwd
	small
	small
	small
	large
	small
	small
	awk -F: 'BEGIN{A=0;B=0} {if($3>100) {A++; print "large"} else {B++; print "small"}} END{print A,"\t",B}' /etc/passwd 
                                                                                                                  //ID大于100,A加1，否则B加1
	awk -F: '{if($3<100) next; else print}'            /etc/passwd                         //小于100跳过，否则显示
	awk -F: 'BEGIN{i=1} {if(i<NF) print NR,NF,i++ }'   /etc/passwd   
	awk -F: 'BEGIN{i=1} {if(i<NF) {print NR,NF} i++ }' /etc/passwd
另一种形式
	awk -F: '{print ($3>100 ? "yes":"no")}'            /etc/passwd 
	awk -F: '{print ($3>100 ? $3":\tyes":$3":\tno")}'  /etc/passwd
 
while语句
	awk -F: 'BEGIN{i=1} {while(i<NF) print NF,$i,i++}' /etc/passwd 
		7 root 1
		7 x 2
		7 0 3
		7 0 4
		7 root 5
		7 /root 6

数组
	netstat -anp | awk 'NR!=1{a[$6]++} END{for (i in a) print i,"\t",a[i]}'
	netstat -anp | awk 'NR!=1{a[$6]++} END{for (i in a) printf "%-20s %-10s %-5s \n", i,"\t",a[i]}'
		9523          1
		9929          1
		LISTEN        6
		7903          1
		3038/cupsd    1
		7913          1
		10837         1
		9833          1

应用1
	awk -F: '{print NF}'                          helloworld.sh                             //输出文件每行有多少字段
	awk -F: '{print $1,$2,$3,$4,$5}'              helloworld.sh                             //输出前5个字段
	awk -F: '{print $1,$2,$3,$4,$5}' OFS='\t'     helloworld.sh                             //输出前5个字段并使用制表符分隔输出
	awk -F: '{print NR,$1,$2,$3,$4,$5}' OFS='\t'  helloworld.sh                             //制表符分隔输出前5个字段，并打印行号
 
应用2
	awk -F'[:#]' '{print NF}'                             helloworld.sh                     //指定多个分隔符: #，输出每行多少字段
	awk -F'[:#]' '{print $1,$2,$3,$4,$5,$6,$7}' OFS='\t'  helloworld.sh                     //制表符分隔输出多字段
 
应用3
	awk -F'[:#/]' '{print NF}'                                      helloworld.sh           //指定三个分隔符，并输出每行字段数
	awk -F'[:#/]' '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12}'  helloworld.sh           //制表符分隔输出多字段
 
应用4
计算/home目录下，普通文件的大小，使用KB作为单位
	ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",sum/1024,"KB"}'
	ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",int(sum/1024),"KB"}'  //int是取整的意思
 
应用5
统计netstat -anp 状态为LISTEN和CONNECT的连接数量分别是多少
	netstat -anp|awk '$6~/LISTEN|CONNECTED/{sum[$6]++} END{for (i in sum) printf "%-10s %-6s %-3s \n", i," ",sum[i]}'
 
应用6
统计/home目录下不同用户的普通文件的总数是多少？
	ls -l|awk 'NR!=1 && !/^d/{sum[$3]++} END{for (i in sum) printf "%-6s %-5s %-3s \n",i," ",sum[i]}'   
	mysql        199 
	root           374 
统计/home目录下不同用户的普通文件的大小总size是多少？
	ls -l|awk 'NR!=1 && !/^d/{sum[$3]+=$5} END{for (i in sum) printf "%-6s %-5s %-3s %-2s \n",i," ",sum[i]/1024/1024,"MB"}'






memoName
